#  -*- coding: UTF-8 -*-
'''
    线性回归
    为什么没有收敛？
'''
import random

class LinearRegression(object):
    def __init__(self, rate = 0.01, iterations = 10):
        self.lr = rate
        self.iterations = iterations
        self.w = 0.0
        self.bias = 0.0
    
    #损失函数， 计算y和y的预测值平方差
    def cost_function(self, X, Y, weight, bias):
        n = len(X)
        total_error = 0.0
        for i in range(n):
            total_error += (Y[i] - (weight * X[i] + bias))**2
        return total_error/n
    
    #计算梯度
    def update_weights(self, X, Y, weight, bias, learning_rate):
        dw = 0
        db = 0
        n = len(X)
        for i in range(n):
            dw += -2 * X[i] * (Y[i] - (weight*X[i] + bias))
            db += -2 * (Y[i] - (weight*X[i] + bias))
        weight -= (dw/n) * learning_rate
        bias -= (db/n) * learning_rate
        return weight, bias

    #随机梯度向下， 计算所有样本计算量太大，只随机的选择部分样本
    def random_update_weight(self, X, Y, weight, bias, learning_rate):
        dw = 0
        db = 0
        n = len(X)
        indexes = [i for i in range(n)]
        random.shuffle(indexes)
        batch_size = 4
        for k in range(batch_size):
            i = indexes[k]
            dw += -2 * X[i] * (Y[i] - (weight*X[i] + bias))
            db += -2 * (Y[i] - (weight*X[i] + bias))
        weight -= (dw/n) * learning_rate
        bias -= (db/n) * learning_rate
        return weight, bias

    def fit(self, X, Y):
        cost_history = []
        for i in range(self.iterations):
            self.w, self.bias = self.random_update_weight(X, Y, self.w, self.bias, self.lr)
            cost = self.cost_function(X, Y, self.w, self.bias)
            cost_history.append(cost)
            if i % 10 == 0:
                print("iter = {:d} weight={:.2f} bias={:.4f} cost={:.2f}".format(i, self.w, self.bias, cost))
        return self.w, self.bias, cost_history

    def predict(self, x):
        x = (x+100)/200
        return self.w*x + self.bias
    
x = [1, 2, 3, 10, 20, 50, 100, -2, -10, -100, -5, -20]
y = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0]
model = LinearRegression(0.01, 50000)
X = [(k+100)/200 for k in x]
model.fit(X, y)

test_x = [90, 80, 81, 82, 75, 40, 32, 15, 5, 1, -1, -15, -20, -22, -33, -45, -60, -90]
for i in range(len(test_x)):
    print('input {} => predict: {}'.format(test_x[i], model.predict(test_x[i])))